/* Copyright 2024 Coremail
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

import type { BaseDao, Property, QueryBuilder } from '@ohos/dataorm';
import { GlobalContext } from '@ohos/dataorm';
import type { FolderData, FolderType } from 'coremail/src/main/ets/api';
import type { Logger } from 'coremail/src/main/ets/utils/log';
import { getLogger } from 'coremail/src/main/ets/utils/log';
import { FolderEntity, FolderId } from '../../entities/FolderEntity';
import type drs from '@ohos.data.relationalStore'
import { getTaskLogger } from '../../utils/LogUtil';

/**
 *
 * @class FolderDaoImpl
 * 在这一层使用的数据实体为FolderEntity
 * 由MailDbDataOrm这一层负责进行数据转换.
 * @brief 文家那几数据的本地操作.
 *
 */
const logger = getLogger("FolerDaoImpl");
const taskLogger = getTaskLogger("InitFolder");
const folderLogger = getTaskLogger("__folder");
const FILE_SEPARATOR = "/";

export class FolderDaoImpl {
  private dao: BaseDao<FolderEntity, number>
  private TAG: string = "FolderDaoImpl";
  private logger: Logger = getLogger(this.TAG)

  constructor(dao: BaseDao<FolderEntity, number>) {
    this.dao = dao;
  }

  /**
   * 获取指定文件夹的子文件夹..
   * @param parent 上级文件夹名称
   * "" 代表是Root目录
   * @returns
   */
  async getFolderList(parent: string): Promise<FolderEntity[]> {
    let logger = getLogger("__folder");
    let parentFoldler: FolderEntity = await this.getFolderByFullName(parent);
    logger.log(`[${this.TAG}] [get folder id by name] [ok] [${parent}==>${parentFoldler.id}]`)
    let arr: FolderEntity[] = await this.getFolderListByParentId(parentFoldler.id);
    // logger.log(`[${this.TAG}] [get sub folder from db] [ok] [${parentFoldler.id}:${JSON.stringify(folders)}]`)
    return arr;
  }

  /**
   * 根据父文件夹id获取子文件节
   * @param parentId
   * @returns
   */
  async getFolderListByParentId(parentId: number): Promise<FolderEntity[]> {
    let entityClass = GlobalContext.getContext().getValue(GlobalContext.KEY_CLS) as Record<string, Object>;
    let properties = entityClass.FolderEntity as Record<string, Property>;
    let queryBuilder: QueryBuilder<FolderEntity> = this.dao.queryBuilder().where(properties['parentId'].eq(parentId))
    let arr: FolderEntity[] = await queryBuilder.list();
    return arr;
  }

  /**
   * 根据文件夹名称找到对应实体.
   * return null 表示本地数据库没有,上层会走catch方法.
   * @param fullName,文件夹的全名
   * eg: 其他文件夹/QQ邮件订阅
   *
   *
   * @returns
   */
  async getFolderByFullName(fullName: string): Promise<FolderEntity|null> {
    if(fullName == "") return FolderEntity.FOLDER_ENTITY_ROOT;
    let names = fullName.split(FILE_SEPARATOR);
    let parentId = 0;
    let folder:FolderEntity|null = null;
    for(var name of names) {
      // let name = names[index];
      folder = await this.getFolderByName(name,parentId);
      if(!folder) return null;
      parentId = folder.id;
    }
    return folder;
  }

  async getImpUnreadCount(): Promise<number> {
    let pRet:Promise<drs.ResultSet> = this.dao.rawQuery(`SELECT COUNT(*) FROM mail WHERE is_new = 1 AND is_flag = 1 `) as Promise<drs.ResultSet>;
    let cursor:drs.ResultSet = await pRet;
    cursor.goToFirstRow()
    let sum:number = cursor.getLong(0);
    return sum;
  }

  async getFolderUnreadCount(folderId:number): Promise<number> {
    let pRet:Promise<drs.ResultSet> = this.dao.rawQuery(`SELECT COUNT(*) FROM mail WHERE is_new = 1 AND folder_id = ? `,[folderId]) as Promise<drs.ResultSet>;
    let cursor:drs.ResultSet = await pRet;
    cursor.goToFirstRow()
    let sum:number = cursor.getLong(0);
    return sum;
  }

  async getFolderMailCount(folderId:number): Promise<number> {
    let pRet = this.dao.rawQuery(`SELECT COUNT(*) FROM mail WHERE folder_id = ? `,[folderId]) as Promise<drs.ResultSet>;
    let cursor = await pRet;
    cursor.goToFirstRow()
    let sum = cursor.getLong(0) as number;
    return sum;
  }




  async getFolderById(id:number): Promise<FolderEntity|null> {
    let entityClass = GlobalContext.getContext().getValue(GlobalContext.KEY_CLS) as Record<string, Object>;
    let properties = entityClass.FolderEntity as Record<string, Property>;
    let queryBuilder: QueryBuilder<FolderEntity> = this.dao.queryBuilder()
      .whereSql(properties['id'].eqSql(id))
    let query = queryBuilder.buildSql();
    let arr: FolderEntity[] = await query.listSql();
    if (arr.length > 0) {
      return arr[0]
    }
    else {
      return null;
    }
  }

  async getFolderByName(name: string,parentFolderId:number): Promise<FolderEntity|null> {
    let logger = getLogger("__folder");
    let entityClass = GlobalContext.getContext().getValue(GlobalContext.KEY_CLS) as Record<string, Object>;
    let properties = entityClass.FolderEntity as Record<string, Property>;
    let queryBuilder: QueryBuilder<FolderEntity> = this.dao.queryBuilder()
      .whereSql(properties['name'].eqSql(name),[properties['parentId'].eqSql(parentFolderId)])
    let query = queryBuilder.buildSql();
    try {
      let arr: FolderEntity[] = await query.listSql();
      if (arr.length > 0) {
        // logger.debug(`[${this.TAG}] [find folder from db] [ok] [${fullName}]`)
        return arr[0]
      }
    } catch (e) {
      logger.error("db failed", e)
    }
    return null;
  }

  /**
   * 插入FolderData到数据库.
   * 返回插入的条数.
   */
  async insertFolderDatas(folderDatas: FolderData[], parentName: string): Promise<void> {
    taskLogger.trace(`[insertFolderDatas],${JSON.stringify(folderDatas)}}`)
    let arr: FolderEntity[] = await this.transformFolderEntity(folderDatas, parentName);
    taskLogger.debug(`[${this.TAG}] [insert sub folder datas to db] [ok] [parent:${parentName},${JSON.stringify(folderDatas)}]`)
    return this.insertFolderEntities(arr);
  }

  /**
   * 插入文件夹实体
   * @param entities
   */
  insertFolderEntities(entities: FolderEntity[]): Promise<void> {
    // taskLogger.trace(`[insertFolderEntities] `)
    return this.dao.insertInTxArrAsync(...entities)
  }


  insertFolder(name:string,parentId:number,unreadCount:number = 0,mailCount:number = 0,type:number = 0):Promise<FolderEntity> {
    taskLogger.trace(`[insertFolder] `)
    let entity = new FolderEntity();
    entity.name = name;
    entity.parentId = parentId;
    entity.unReadCount = unreadCount;
    entity.mailCount = mailCount;
    entity.type = type;
    return this.dao.insert(entity)
      .then(()=>{
        return entity;
      }).catch((e)=>{
       return Promise.reject(`insert failed, ${e}`);
    });
  }




  /**
   * 插入文件夹,需要根据节点一个一个添加.
   *
   * INBOX/a/b/c
   *
   * 1,如果路径父目录不存在,先创建再插入.
   * 2,如果文件夹已经存在,则更新.
   * 3,未读数,刚创建的设置为0,已经存在的,根据实际情况查询.
   * @param fullName
   */
  async insertOrUpdateFolderWithFullName(fullName:string,nodeFolder:FolderEntity):Promise<void> {
    // taskLogger.trace(`[insertOrUpdateFolder]`)
    await new Promise<void>(async (resolve) => {
      let entity = await this.getFolderByFullName(fullName);
      if (entity) {
        entity.mailCount = nodeFolder.mailCount;
        // entity.unReadCount = nodeFolder.unReadCount;
        entity.unReadCount = await this.getFolderUnreadCount(entity.id);
        entity.type = nodeFolder.type;
        folderLogger.debug(`[insertOrUpdateFolderWithFullName][update folder: ${fullName}]`);
        await this.updateFolder(entity);
        resolve()
      }
      //需要去除前导/
      if (fullName.startsWith("/")) {
        fullName = fullName.replace("/", "");
      }
      let names = fullName.split('/');
      let parentId = 0;
      for (let name of names) {
        let entity = await this.getFolderByName(name, parentId);
        if (!entity) {
          folderLogger.debug(`[insertOrUpdateFolderWithFullName][insert folder:${fullName}]`);
          // let unReadCount = nodeFolder.unReadCount;
          let unReadCount = 0;
          entity = await this.insertFolder(name, parentId, unReadCount, nodeFolder.mailCount, nodeFolder.type);
        }
        parentId = entity.id;
      }
      resolve();
    });
  }

  /**
   * 重命名文件夹.
   * @param folderName
   * @param newName
   * @returns
   */
  async renameFolder(fullName: string, newFullName: string): Promise<void> {
    let entity = await this.getFolderByFullName(fullName);
    if(entity) {
      let newName = this.getName(newFullName);
      entity.name = newName;
      return this.dao.updateInTxAsync(entity);
    }
    return Promise.reject("no such folder");
  }




  async updateFolder(folder:FolderEntity): Promise<void> {
    await this.dao.updateAsync(folder);
  }

  deleteFolder(folder:FolderEntity): Promise<void> {
    return this.dao.deleteAsync(folder);
  }

  async deleteFolderByFullName(fullName: string): Promise<void> {
    let entity = await this.getFolderByFullName(fullName);
    if(entity) {
     return  this.dao.deleteAsync(entity);
    }
    else {
      return Promise.reject("no such folder");
    }
  }

  async getFullName(entity:FolderEntity):Promise<string> {
    let names = [];
    names.push(entity.name);

    while(entity.id != FolderId.ROOT) {
      entity = await this.getFolderById(entity.parentId);
      names.push(entity.name);
    }

    names.reverse();

    let fullName = names.join("/");
    if (fullName.startsWith("/")) {
      fullName = fullName.substring(1);
    }

    return fullName;
  }


  /**
   * 获取显示名称
   * @param fullName
   */
  getName(fullName:string) :string {
    return fullName.split(FILE_SEPARATOR).pop();
  }
  /**
   * 获取父目录全名
   * @param fullName
   */
  getParentFullName(fullName:string) :string {
    let names = fullName.split(FILE_SEPARATOR);
    names.pop()
    return names.join(FILE_SEPARATOR);
  }

  async getCount():Promise<number> {
    let pRet = null;
    pRet = this.dao.rawQuery(`SELECT COUNT(*) FROM folder`) as Promise<drs.ResultSet>;
    let cursor = await pRet  as drs.ResultSet;
    cursor.goToFirstRow()
    let sum = cursor.getLong(0);
    return sum;
  }


  /**
   * 只有存在父节点的情况下,才能插入子节点.
   * FolderData ==> FolderEntity
   * 系统文件夹的level为1.
   * @param data
   * @returns
   */
  transformFolderEntity(datas: FolderData[], parentName: string): Promise<FolderEntity[]> {
    return this.getFolderByFullName(parentName).then((parentEntity: FolderEntity) => {
      let entities = datas.map((data: FolderData, index: number) => {
        let entity = new FolderEntity();
        entity.parentId = parentEntity.id;
        entity.name = data.name;
        // entity.fullName = data.fullName;
        entity.unReadCount = data.unreadCount;
        entity.mailCount = data.mailCount;
        entity.level = parentEntity.level + 1;
        entity.orderNum = index;
        entity.type = data.type;
        return entity;
      })
      return entities;
    })

  }

  /**
   * 初始需要插入一个Root文件夹.
   */
  insertRootFolder(): Promise<void> {
    return this.insertFolderEntities([FolderEntity.FOLDER_ENTITY_ROOT]);
  }

  /**
   * 数据库中创建子文件夹.
   * 1,找到父文件夹的id
   * 2,生成新的entity,并插入进去.
   * @param folderName
   * @param parent
   * @returns
   */
  async createSubFolder(folderName: string, parentFullName: string,unreadCount = 0 ,mailCount = 0,type:FolderType = 0): Promise<void> {
    taskLogger.trace(`[__newfolder] [create sub folder] [parent:${parentFullName},folderName:${folderName}]`)
    let parentEntity: FolderEntity = await this.getFolderByFullName(parentFullName);
    let entity = new FolderEntity();
    entity.parentId = parentEntity.id;
    entity.name = folderName;
    entity.level = parentEntity.level + 1;
    entity.unReadCount = unreadCount;
    entity.mailCount = mailCount;
    entity.type = type;
    //orderNum 为当前parent的总数+1.
    entity.orderNum = await this.getFolderSubCount(parentFullName) + 1;
    await this.insertFolderEntities([entity]);
  }

  /**
   * 获取当前文件夹的子文件夹数量
   * @param fullName 当前文件夹的fullName.
   * @returns
   */
  async getFolderSubCount(fullName: string): Promise<number> {
    let queryStr = (`SELECT count(*) FROM tbFolder WHERE full_name = ?`);
    let arg = [fullName];
    // 使用whereOr多条件查询无效，此处选择sql语句实现
    let cursor = await this.dao.rawQuery(queryStr, arg) as drs.ResultSet;
    if (cursor != null && cursor.rowCount > 0) {
      cursor.goToFirstRow()
      let sum = cursor.getLong(0);
      return sum;
    }
    else {
      return 0;
    }
  }
}